#include <sys/ptrace.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>

/*some boring utility functions*/
void writeError(char *str);
void writeOutUInt(unsigned num);
void writeOut(char *str);

int startCounting(int pid);
int startExternalProgram(int argc, char **argv);

int main(int argc, char **argv) {
  int childPid;
  
  if (argc < 2) {
    writeError("At leat on argument is required. Usage: icounter [program_name] [program_parameters]\n");
    return -1;
  }
  
  childPid = fork();
  if (childPid > 0) {
    //parent
    return startCounting(childPid);
  } else {
    //child
    return startExternalProgram(argc-1, argv+1);
  }
  
}

int startCounting(int pid) {
  unsigned icounter = 0;
  int waitStatus;
  
  wait(&waitStatus);
  while (WIFSTOPPED(waitStatus)) {
    icounter++;
    
    if (ptrace(PTRACE_SINGLESTEP, pid, 0, 0) < 0) {
      writeError("ptrace signle step error");
    }
    
    wait(&waitStatus);
  }
  
  writeOut("\n---------------------------\n");
  writeOutUInt(icounter);
  writeOut(" instructions executed.\n");
  writeOut("---------------------------\n");
  
  return 0;
}

int startExternalProgram(int argc, char **argv) {
 if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0) {
   writeError("ptrace is unable to trace the child process");
   return;
 }
 
 return execv(argv[0], argv);
}

//utilities implementation

void writeOutUInt(unsigned num) {
  char res[20];
  unsigned tmp = num;
  int len = 0;
  do {tmp /= 10; ++len;} while (tmp!=0);
  
  char *s = res + len;
  do {
    *--s = '0' + (num % 10);
    num /= 10;
  } while (num != 0);
  
  write(1, res, len);
}

void writeOut(char *str) {
  write(1, str, strlen(str));
}

void writeError(char *str) {
  write(2, str, strlen(str));
}